1 /* 2 * Hunt - a framework for web and console application based on Collie using Dlang development 3 * 4 * Copyright (C) 2015-2017 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: HuntLabs 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.text.configuration; 13 14 import std.traits; 15 import std.conv; 16 import std.exception; 17 import std.array; 18 import std.stdio; 19 import std.string; 20 import kiss.logger; 21 22 class ConfFormatException : Exception 23 { 24 mixin basicExceptionCtors; 25 } 26 27 class NoValueHasException : Exception 28 { 29 mixin basicExceptionCtors; 30 } 31 32 class Configuration 33 { 34 class ConfigurationValue 35 { 36 @property value(string name){ 37 auto v = _map.get(name, null); 38 enforce!NoValueHasException(v,format(" %s is not in config! ",name)); 39 return v; 40 } 41 42 @property value(){ 43 return _value; 44 } 45 46 auto opCast(T)(){ 47 static if(is(T == bool)) 48 return as!bool(true); 49 else static if(isSomeString!T) 50 return cast(T)(value()); 51 else static if(isNumeric!(T)) 52 return as!T(T.init); 53 else 54 static assert(0,"not support type"); 55 } 56 57 auto as(T)(T value = T.init) if(isNumeric!(T)) 58 { 59 if(_value.length == 0) 60 return value; 61 else 62 return to!T(_value); 63 } 64 65 auto as(T : bool)(T value = T.init) 66 { 67 if(_value.length == 0 || _value == "false" || _value == "0") 68 return false; 69 else 70 return true; 71 } 72 73 auto as(T : string)(T value = T.init) 74 { 75 if(_value.length == 0) 76 return value; 77 else 78 return _value; 79 } 80 81 auto opDispatch(string s)() 82 { 83 return value(s); 84 } 85 86 private : 87 string _value; 88 ConfigurationValue[string] _map; 89 } 90 91 this(string filename, string section = "") 92 { 93 _section = section; 94 loadConfig(filename); 95 } 96 97 @property value(string name){ 98 return _value.value(name); 99 } 100 101 auto opDispatch(string s)() 102 { 103 return _value.opDispatch!(s)(); 104 } 105 106 private: 107 void loadConfig(string filename) 108 { 109 _value = new ConfigurationValue(); 110 111 import std.file; 112 if(!exists(filename)) 113 { 114 throw new Exception("The config file doesn't exist: " ~ filename); 115 } 116 import std.format; 117 auto f = File(filename,"r"); 118 if(!f.isOpen()) return; 119 scope(exit) f.close(); 120 string section = ""; 121 int line = 1; 122 while(!f.eof()) 123 { 124 scope(exit) line += 1; 125 string str = f.readln(); 126 str = strip(str); 127 if(str.length == 0) continue; 128 if(str[0] == '#' || str[0] == ';') continue; 129 auto len = str.length -1; 130 if(str[0] == '[' && str[len] == ']') 131 { 132 section = str[1..len].strip; 133 continue; 134 } 135 if(section != _section && section != "") 136 continue;// 不是自己要读取的分段,就跳过 137 auto site = str.indexOf("="); 138 enforce!ConfFormatException((site > 0),format("the format is erro in file %s, in line %d",filename,line)); 139 string key = str[0..site].strip; 140 setValue(split(key,'.'),str[site + 1..$].strip); 141 } 142 } 143 144 void setValue(string[] list, string value) 145 { 146 auto cvalue = _value; 147 foreach(ref str ; list){ 148 if(str.length == 0) continue; 149 auto tvalue = cvalue._map.get(str,null); 150 if(tvalue is null){ // 不存在就追加一个 151 tvalue = new ConfigurationValue(); 152 cvalue._map[str] = tvalue; 153 } 154 cvalue = tvalue; 155 } 156 if(cvalue is _value) 157 return; 158 cvalue._value = value; 159 } 160 161 private: 162 string _section; 163 ConfigurationValue _value; 164 } 165 166 167 unittest 168 { 169 import std.stdio; 170 171 import FE = std.file; 172 173 FE.write("test.config","http.listen = 100 \napp.test = \n# this is \n ; start dev\n [dev]\napp.test = dev"); 174 auto conf = new Configuration("test.config"); 175 assert(conf.http.listen.as!long() == 100); 176 assert(conf.app.test.value() == ""); 177 178 auto confdev = new Configuration("test.config","dev"); 179 long tv = cast(long)confdev.http.listen; 180 assert(tv == 100); 181 assert(confdev.http.listen.as!long() == 100); 182 writeln("----------" ,confdev.app.test.value()); 183 184 string tvstr = cast(string)confdev.app.test; 185 //auto tvstrw = cast(wstring)confdev.app.test; 186 187 assert(tvstr == "dev"); 188 assert(confdev.app.test.value() == "dev"); 189 bool tvBool = cast(bool)confdev.app.test; 190 assert(tvBool); 191 192 string str; 193 auto e = collectException!NoValueHasException(confdev.app.host.value(), str); 194 assert(e && e.msg == " host is not in config! "); 195 196 }